﻿using System.IO;

namespace MyUtils.Core
{
    public enum MyLogLevel
    {
        None = -1,
        Debug = 0,
        Info = 1,
        Error = 2,
    }
    
    /// <summary>
    /// 日志类(禁止继承)
    /// </summary>
    public sealed class MyLog
    {
        #region 字段
        /// <summary>
        /// 忽略的最小等级 低于这个等级的不做记录
        /// </summary>
        private int _ignoreMinLevel = -1;
        public int IgnoreMinLevel { get { return _ignoreMinLevel; } }
        /// <summary>
        /// 是否输出到控制台
        /// </summary>
        private bool _useConsole = false;
        public bool UseConsole { get { return _useConsole; } }
        

        /// <summary>
        /// 日志记录的文件目录
        /// </summary>
        private string _logFilePath = Directory.GetCurrentDirectory();
        public string LogFileUrl { get { return _logFilePath; } }

        /// <summary>
        /// 日志文件记录的标签
        /// </summary>
        private string _logTag = "Tmp";
        public string LogTag { get { return _logTag; } }
        /// <summary>
        /// 文件锁
        /// </summary>
        private  Mutex _mutex;
        /// <summary>
        /// 任务工厂
        /// </summary>
        private TaskFactory _taskFactory;
        #endregion

        #region 构造函数
        /// <summary>
        /// 基础构建
        /// </summary>
        private void baseInit()
        {
            var logdir = Path.Combine(_logFilePath, _logTag);
            if (Directory.Exists(logdir) == false)
                Directory.CreateDirectory(logdir);
            _mutex = new Mutex(false, $"{_logTag}{DateTime.Now.GetTimeStamp()}");
            _taskFactory = new TaskFactory(new LimitedConcurrencyLevelTaskScheduler(3));
        }

        public MyLog()
        {
            baseInit();
        }
        public MyLog(string logTag,string logFilePath = "",bool useConsole = false,MyLogLevel ignoreMinLevel = MyLogLevel.None)
        {
            _logTag = logTag;
            if (string.IsNullOrEmpty(logFilePath) == false)
                _logFilePath = logFilePath;
            _useConsole = useConsole;
            _ignoreMinLevel = (int)ignoreMinLevel;
            baseInit();
        }
        #endregion


        #region 私有工具
        /// <summary>
        /// 获取日志文件名
        /// </summary>
        /// <returns></returns>
        private string getLogFilePath()
        {
            var nowStr = DateTime.Now.ToString("yyyyMMdd");
            var logFileName = $"{Path.Combine(_logFilePath,_logTag,nowStr)}.log";
            return logFileName;
        }
        /// <summary>
        /// 获取写入文件的内容(如果被忽略则直接)
        /// </summary>
        /// <param name="level"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        private string getWriteStr(MyLogLevel level,string msg)
        {
            return string.Format(@"{0} {1} {2}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), ("[" + level.ToString() + "]").PadRight(7, ' '), msg);
        }
        /// <summary>
        /// 写入文件
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        private string writeFile(string msg,string path)
        {
            FileStream fs = null;
            StreamWriter sw = null;
            var ret = "";
            try
            {
                fs = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
                sw = new StreamWriter(fs);
                fs.Seek(0, SeekOrigin.End);
                sw.WriteLine(msg);
                sw.Flush();
                fs.Flush();
            }
            catch (Exception e) 
            {
                ret = e.Message;
            }
            finally
            {
                if (sw != null)
                    sw.Close();
                if ( fs != null)
                    fs.Close();
            }
            return ret;
        }
        #endregion

        #region 对外方法
        public Task? Debug(string msg)
        {
            if (_ignoreMinLevel >= (int)MyLogLevel.Debug)
                return null;
            return _taskFactory.StartNew(() => 
            {
                try
                {
                    _mutex.WaitOne();
                    msg = getWriteStr(MyLogLevel.Debug,msg);
                    string filePath = getLogFilePath();
                    writeFile(msg, filePath);
                    if (_useConsole)
                        Console.WriteLine(msg);
                }
                catch (Exception e) 
                {
                }
                finally
                {
                    _mutex.ReleaseMutex();
                }
            });
        }

        public Task? Info(string msg)
        {
            if (_ignoreMinLevel >= (int)MyLogLevel.Info)
                return null;
            return _taskFactory.StartNew(() =>
            {
                try
                {
                    _mutex.WaitOne();
                    msg = getWriteStr(MyLogLevel.Debug, msg);
                    string filePath = getLogFilePath();
                    writeFile(msg, filePath);
                    if (_useConsole)
                        Console.WriteLine(msg);
                }
                catch (Exception e)
                {
                }
                finally
                {
                    _mutex.ReleaseMutex();
                }
            });
        }

        public Task? Error(string msg)
        {
            if (_ignoreMinLevel >= (int)MyLogLevel.Error)
                return null;
            return _taskFactory.StartNew(() =>
            {
                try
                {
                    _mutex.WaitOne();
                    msg = getWriteStr(MyLogLevel.Debug, msg);
                    string filePath = getLogFilePath();
                    writeFile(msg, filePath);
                    if (_useConsole)
                        Console.WriteLine(msg);
                }
                catch (Exception e)
                {
                }
                finally
                {
                    _mutex.ReleaseMutex();
                }
            });
        }
        #endregion
    }
}
